package com.icss.flow.service.impl;

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.icss.flow.exception.FlowException;
import com.icss.flow.pojo.dto.DelegateTaskDTO;
import com.icss.flow.pojo.dto.FlowDTO;
import com.icss.flow.pojo.dto.TMetaEntityDTO;
import com.icss.flow.pojo.entity.ActHiTaskinst;
import com.icss.flow.pojo.entity.TMetaEntity;
import com.icss.flow.pojo.vo.*;
import com.icss.flow.service.ActHiTaskinstService;
import com.icss.flow.service.CommEntityService;
import com.icss.flow.service.FlowService;
import com.icss.flow.service.TMetaEntityService;
import com.icss.flow.utils.FormUtils;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.camunda.bpm.engine.*;
import org.camunda.bpm.engine.exception.NullValueException;
import org.camunda.bpm.engine.form.FormField;
import org.camunda.bpm.engine.history.HistoricTaskInstance;
import org.camunda.bpm.engine.history.HistoricTaskInstanceQuery;
import org.camunda.bpm.engine.impl.persistence.entity.TaskEntity;
import org.camunda.bpm.engine.repository.Deployment;
import org.camunda.bpm.engine.repository.ProcessDefinition;
import org.camunda.bpm.engine.repository.ProcessDefinitionQuery;
import org.camunda.bpm.engine.runtime.ProcessInstance;
import org.camunda.bpm.engine.task.DelegationState;
import org.camunda.bpm.engine.task.Task;
import org.camunda.bpm.engine.task.TaskQuery;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.system.api.RemoteUserService;
import org.dromara.system.api.model.LoginUser;
import org.dromara.system.api.model.SysUserDTO;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author hyk
 * @Data 2024/3/13
 */
@Slf4j
@Service("flowService")
@AllArgsConstructor
public class FlowServiceImpl implements FlowService {
    private static final String CLASS_NAME = "[Flow Service Impl] ";
    private HistoryService historyService;
    private RepositoryService repositoryService;
    private TMetaEntityService tMetaEntityService;
    private CommEntityService commEntityService;
    private RuntimeService runtimeService;
    private TaskService taskService;
    private FormService formService;
    private ActHiTaskinstService actHiTaskinstService;
    @DubboReference
    private RemoteUserService remoteUserService;





    /**
     * @param flowDTO 流程定义
     * @return
     * @throws FlowException
     */
    @SneakyThrows
    @Override
    @Transactional(rollbackFor = FlowException.class)
    public Boolean deployFlow(FlowDTO flowDTO) {
        // 保存资源文件
        LoginUser loginUser = LoginHelper.getLoginUser();
        // 将流程图进行解密处理
        byte[] decode = Base64.getDecoder().decode(flowDTO.getProcessResources());
        repositoryService.createDeployment().tenantId(loginUser.getTenantId()).addString(flowDTO.getFlowName() + ".bpmn", new String(decode)).name(flowDTO.getFlowName()).deploy();
        return true;
    }

    /**
     * 发布流程
     * @param flowDTO 实体流程定义
     * @return
     */
    @Override
    public Boolean deployEntity(FlowDTO flowDTO) {
        // 保存资源文件
        LoginUser loginUser = LoginHelper.getLoginUser();

        TMetaEntityVO entityDetail = tMetaEntityService.getEntityDetail(flowDTO.getEntityCode());


        // 将流程图进行解密处理
        byte[] decode = Base64.getDecoder().decode(flowDTO.getProcessResources());
        Deployment deploy = repositoryService.createDeployment().tenantId(loginUser.getTenantId()).addString(entityDetail.getPhysicalName() + ".bpmn", new String(decode)).name(entityDetail.getPhysicalName()).deploy();
        String flowKey = repositoryService.createProcessDefinitionQuery().deploymentId(deploy.getId()).singleResult().getKey();
        tMetaEntityService.update(new LambdaUpdateWrapper<>() {{
            eq(TMetaEntity::getEntityCode, flowDTO.getEntityCode()).set(TMetaEntity::getFlowKey, flowKey);
        }});
        return true;
    }

    @Override
    public void selectFlowByEntityId() {

    }


    /**
     * 发起流程
     *
     * @param entityCode 流程key
     * @param id
     * @param params     表单字段
     * @return
     */

    @Override
    public Boolean launchFlow(Integer entityCode, Integer id, Map<String, Object> params) {

        TMetaEntityDTO tMetaEntityDTO = tMetaEntityService.queryById(entityCode);

        // 检查流程是否为空
        String flowId = getProcessDefinition(tMetaEntityDTO.getFlowKey()).getId();
        // 表单字段必填项检查
        List<FormField> formFields = formService.getStartFormData(flowId).getFormFields();
        // 查字段是否有没有被填写
        FormUtils.verifyForm(params, formFields);
        ProcessInstance processInstance = runtimeService.startProcessInstanceById(flowId, params);

        if (processInstance != null) {
            params.put("flowId", processInstance.getProcessInstanceId());
        }
        commEntityService.updateEntity(entityCode, id, params);
        return true;
    }


    @Override
    public void claimTask() {

    }

    @Override
    public void returnTask() {

    }

    /**
     * 获取任务列表
     *
     * @param pageQuery 分页条件
     * @return
     */

    @Override
    public TableDataInfo<TodoVO> queryToDoTaskList(PageQuery pageQuery) {
        LoginUser loginUser = LoginHelper.getLoginUser();

        TaskQuery taskQuery = taskService.createTaskQuery().
                // 租户
                        tenantIdIn(loginUser.getTenantId()).
                // 已下所有的条件都是或
                        or().
                // 当前处理人
                        taskAssignee(loginUser.getUserId().toString()).
                // 处理组
                        taskCandidateGroup(loginUser.getDeptId().toString()).
                // 任务所拥有人
                        taskOwner(loginUser.getUserId().toString()).
                // 结束或
                        endOr();


        // 获取数据的总条数
        long count = taskQuery.count();
        // 分页
        List<Task> taskList = taskQuery.orderByTaskCreateTime().desc().orderByLastUpdated().desc().listPage(pageQuery.getPageNum() - 1, pageQuery.getPageSize());
        // 获取让所有的事件id
        String[] processDefinitionIdList = taskList.stream().map(Task::getProcessDefinitionId).toArray(String[]::new);
        // 获取所有的事件详情
        List<ProcessDefinition> processDefinitionList = repositoryService.createProcessDefinitionQuery().processDefinitionIdIn(processDefinitionIdList).list();
        // 将所有的事件详情转换事件id与事件详情对应
        Map<String, ProcessDefinition> processDefinitionMap = processDefinitionList.stream().collect(Collectors.toMap(ProcessDefinition::getId, s -> s));
        // 转换成vo类
        List<TodoVO> todoVOList = taskList.stream().map(task -> {
            TodoVO todoVO = new TodoVO();
            todoVO.setFlowName(processDefinitionMap.get(task.getProcessDefinitionId()).getName());
            todoVO.setTaskId(task.getId());
            todoVO.setCreateTime(task.getCreateTime());
            todoVO.setDueDate(task.getDueDate());
            todoVO.setDelegationState(Objects.equals(task.getAssignee(), loginUser.getUserId().toString()) ? null : task.getDelegationState());
            return todoVO;
        }).toList();
        return new TableDataInfo<>(todoVOList, count);


    }

    /**
     * 查询历史待办任务列表
     *
     * @param pageQuery
     * @return
     */

    @Override
    public TableDataInfo<?> queryHistoryToDoTaskList(PageQuery pageQuery) {
        LoginUser loginUser = LoginHelper.getLoginUser();

        HistoricTaskInstanceQuery historicTaskInstanceQuery = historyService.createHistoricTaskInstanceQuery().
                // 租户
                        tenantIdIn(loginUser.getTenantId()).or().
                // 当前处理人
                        taskAssignee(loginUser.getUserId().toString()).
                // 任务所拥有人
                        taskOwner(loginUser.getUserId().toString()).
                // 处理组
                        taskHadCandidateGroup(loginUser.getDeptId().toString()).endOr().taskAssigned().finished();


        // 获取数据的总条数
        long count = historicTaskInstanceQuery.count();
        // 分页
        List<HistoricTaskInstance> historicTaskInstances = historicTaskInstanceQuery.orderByHistoricTaskInstanceEndTime().desc().listPage(pageQuery.getPageNum() - 1, pageQuery.getPageSize());
        // 获取让所有的事件id
        String[] processDefinitionIdList = historicTaskInstances.stream().map(HistoricTaskInstance::getProcessDefinitionId).toArray(String[]::new);
        // 获取所有的事件详情
        List<ProcessDefinition> processDefinitionList = repositoryService.createProcessDefinitionQuery().processDefinitionIdIn(processDefinitionIdList).list();
        // 将所有的事件详情转换事件id与事件详情对应
        Map<String, ProcessDefinition> processDefinitionMap = processDefinitionList.stream().collect(Collectors.toMap(ProcessDefinition::getId, s -> s));
        // 转换成vo类
        List<TodoVO> todoVOList = historicTaskInstances.stream().map(task -> {
            TodoVO todoVO = new TodoVO();
            todoVO.setFlowName(processDefinitionMap.get(task.getProcessDefinitionId()).getName());
            todoVO.setTaskId(task.getId());
            todoVO.setCreateTime(task.getStartTime());
            todoVO.setDueDate(task.getDueDate());
            return todoVO;
        }).toList();
        return new TableDataInfo<>(todoVOList, count);
    }


    @Override
    public void queryToDoTaskDetail() {

    }

    @Override
    public void processTasks() {

    }

    /**
     * 根据任务id查询流程走向
     *
     * @param taskId 任务id
     * @return
     */

    @Override
    public List<HistoricActivityInstanceVO> queryHistoryByTaskId(String taskId) {
        // 获取登录用户
        LoginUser loginUser = LoginHelper.getLoginUser();


        // 获取当前任务的实例
        HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery().taskId(taskId).singleResult();
        // 获取流程实例id
        String processInstanceId = Optional.ofNullable(historicTaskInstance).orElseThrow(() -> new FlowException("没有此任务")).getProcessInstanceId();
        // 获取所有的任务
        List<HistoricTaskInstance> taskInstanceList = historyService.createHistoricTaskInstanceQuery().processInstanceId(processInstanceId).orderByHistoricActivityInstanceStartTime().asc().list();
        // 转换成任务id与任务对应
        Map<String, HistoricTaskInstance> taskInstanceMap = taskInstanceList.stream().collect(Collectors.toMap(HistoricTaskInstance::getId, s -> s));

        // 获取所有的任务审批人的id
        String[] userIdList = taskInstanceList.stream().map(s -> new String[]{s.getAssignee(), s.getOwner()}).flatMap(Arrays::stream).toArray(String[]::new);
        Map<String, SysUserDTO> userDTOMap = remoteUserService.selectUserListByIds(userIdList, loginUser.getTenantId()).stream().collect(Collectors.toMap(sysUserDTO -> sysUserDTO.getUserId().toString(), s -> s));

        return taskInstanceList.stream().map(taskInstance -> new HistoricActivityInstanceVO() {{
            // 节点是否有审批人
            HistoricTaskInstance historicTaskInstance = taskInstanceMap.get(taskInstance.getId());
//            historicTaskInstance.get
            if (ObjectUtils.isNotEmpty(historicTaskInstance)) {
                setApproves(new ApprovesVO() {{
                    setCommitter(userDTOMap.get(historicTaskInstance.getOwner()));
                    setApprover(userDTOMap.get(historicTaskInstance.getAssignee()));
                }});
            }
            setTaskId(taskInstance.getId());
            setNodeName(taskInstance.getName());
            setStartTime(taskInstance.getStartTime());
            setEndTime(taskInstance.getEndTime());
        }}).collect(Collectors.toList());

    }


    /**
     * 根据实体code返回xml文件数据
     *
     * @param entityCode 流程key
     * @return xml文件数据
     */


    @Override
    public Map<String, Object> getEntityBpmn(Integer entityCode) {
        TMetaEntityVO entityDetail = tMetaEntityService.getEntityDetail(entityCode);
        // 获取流程实例对像
        return getBpmn(entityDetail.getFlowKey());

    }

    /**
     * 根据流程key返回xml文件数据
     *
     * @param flowKey 流程key
     * @return xml文件数据
     */

    @SneakyThrows
    @Override
    public Map<String, Object> getBpmn(String flowKey) {
        // 获取流程实例对像
        ProcessDefinition processDefinition = getProcessDefinition(flowKey);
        // 根据流程实例对象的流程id和流程名获取对应的xml文件数据
        InputStream resourceAsStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), processDefinition.getResourceName());
        return new HashMap<>() {{
            put("xml", new String(resourceAsStream.readAllBytes(), StandardCharsets.UTF_8));
        }};

    }


    /**
     * 查询所有的流程信息
     * @param pageQuery 分页
     * @return 流程信息列表
     */

    @Override
    public TableDataInfo<ProcessDefinitionVO> queryFlowList(PageQuery pageQuery) {
        LoginUser loginUser = LoginHelper.getLoginUser();

        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery()
                // 租户
                .tenantIdIn(loginUser.getTenantId())
                // 最新版本
                .latestVersion();

        long count = processDefinitionQuery.count();
        // 查询所有创建的流程
        List<ProcessDefinition> processDefinitionList = processDefinitionQuery.
                // 分页构建
                        listPage(pageQuery.getPageNum() - 1, pageQuery.getPageSize());
        // 转换成vo类型
                    List<ProcessDefinitionVO> processDefinitionVOList =    processDefinitionList.stream().map(processDefinition -> new ProcessDefinitionVO() {
                    {
                        // 原生的不支持序列化，转换成json会报错
                        setId(processDefinition.getDeploymentId());
                        setName(processDefinition.getName());
                        setVersion(processDefinition.getVersion());
                        setTenantId(processDefinition.getTenantId());
                        setSource(processDefinition.getDescription());
                        setKey(processDefinition.getKey());
                    }
        }).collect(Collectors.toList());
        return new TableDataInfo<>(processDefinitionVOList, count);
    }

    @Override
    public String selectFlowByFlowId() {
//        runtimeService.createProcessInstanceQuery().singleResult().
        return "";
    }


    /**
     * 返回不是为空的流程实例对象
     *
     * @param key 流程key
     * @return 流程实例对象
     */

    private ProcessDefinition getProcessDefinition(String key) throws NullValueException {
        LoginUser loginUser = LoginHelper.getLoginUser();
        return Optional.ofNullable(repositoryService.
                        // 创建流程查询
                                createProcessDefinitionQuery().
                        // 根据流程key获取
                                processDefinitionKey(key).
                        // 租户
                                tenantIdIn(loginUser.getTenantId()).
                        // 获取流程对应的最新版本
                                latestVersion().singleResult()).
                // 如果流程为空就抛出异常，终止当前业务
                        orElseThrow(() -> {
                    log.warn("{}------------------>流程{}不存在", CLASS_NAME, key);
                    return new FlowException("流程'" + key + "'不存在");
                });
    }

    /**
     * 将任务进行委托
     *
     * @param delegateTaskDTO 委托实体
     * @return
     */

    @Override
    public Boolean delegateTask(DelegateTaskDTO delegateTaskDTO) {
        // 获取登录用户
        LoginUser loginUser = LoginHelper.getLoginUser();
        // 不能委托给自己
        if (Objects.equals(loginUser.getUserId().toString(), delegateTaskDTO.getUserId())) {
            log.warn("{}------------------>任务‘{}’不能委托给‘{}’",CLASS_NAME,delegateTaskDTO.getTaskId(),delegateTaskDTO.getUserId());
            throw new FlowException("不能委托给自己");
        }
        // 查询任务详情
        Task task = taskService.createTaskQuery().taskId(delegateTaskDTO.getTaskId()).taskAssignee(loginUser.getUserId().toString()).singleResult();
        Optional.ofNullable(task).orElseThrow(() -> {
            log.warn("{}------------------>用户'{}'没有权限分配任务‘{}’",CLASS_NAME,loginUser.getUsername(),delegateTaskDTO.getTaskId());
           return new FlowException("你没有权限分配此任务");
        });
        HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery().taskId(delegateTaskDTO.getTaskId()).singleResult();
        TaskEntity subTask = createSubTask(task, delegateTaskDTO.getUserId(), task.getAssignee(), historicTaskInstance);
        // 完成子任务
        taskService.complete(subTask.getId());
        // 更新父任务
        task.setOwner(null);
        task.setAssignee(delegateTaskDTO.getUserId());
        // 委托状态
        task.setDelegationState(DelegationState.PENDING);
        taskService.saveTask(task);
        ActHiTaskinst actHiTaskinst = new ActHiTaskinst();
        actHiTaskinst.setId(task.getId());
        actHiTaskinst.setStartTime(new Date());
        actHiTaskinstService.updateById(actHiTaskinst);
        return true;
    }

    @Override
    public Boolean taskTransfer() {

        Task task = taskService.createTaskQuery().taskId("").singleResult();
        return null;
    }

    @Override
    public Boolean completeTask(String taskId, Map<String, Object> params) {
        // 获取登录用户
        LoginUser loginUser = LoginHelper.getLoginUser();
        // 查询任务详情
        Task task = taskService.createTaskQuery().taskId(taskId).taskAssignee(loginUser.getUserId().toString()).singleResult();
        Optional.ofNullable(task).orElseThrow(() -> {
            log.warn("{}------------------>用户'{}'没有权限完成任务‘{}’", CLASS_NAME, loginUser.getUsername(), taskId);
            return new FlowException("你没有权限完成此任务");
        });
        List<FormField> formFields = formService.getTaskFormData(taskId).getFormFields();
        // 查字段是否有没有被填写
        FormUtils.verifyForm(params, formFields);
        taskService.complete(taskId, params);
        return true;
    }

    private TaskEntity createSubTask(Task parentTask, String assignee, String owner, HistoricTaskInstance historicTaskInstance) {
        TaskEntity task = null;
        if(Objects.nonNull(parentTask)){
            //1.生成子任务
            task = (TaskEntity) taskService.newTask();
            task.setDescription(parentTask.getDescription());
            task.setTenantId(parentTask.getTenantId());
            task.setOwner(owner);
            task.setAssignee(assignee);
            task.setName(parentTask.getName());
            task.setProcessDefinitionId(parentTask.getProcessDefinitionId());
            task.setProcessInstanceId(parentTask.getProcessInstanceId());
            task.setTaskDefinitionKey(parentTask.getTaskDefinitionKey());
            task.setPriority(parentTask.getPriority());
            task.setCreateTime(historicTaskInstance.getStartTime());
            taskService.saveTask(task);
            ActHiTaskinst actHiTaskinst = new ActHiTaskinst();
            actHiTaskinst.setId(task.getId());
            actHiTaskinst.setStartTime(historicTaskInstance.getStartTime());
            // 关闭tenantid
            actHiTaskinstService.updateById(actHiTaskinst);
        }
        return task;
    }
}
